from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib import messages
from .models import Host, Alert, NetworkDevice, IDC, Rack, Device, Connection, Asset, AssetCategory, ResponsiblePerson, AlertNotification, Cabinet, Server, Switchboard, DeviceType
from django.http import JsonResponse, HttpResponse
from django.views.decorators.http import require_POST, require_http_methods
from django.shortcuts import get_object_or_404
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin
from .forms import HostForm, NetworkDeviceForm, IDCForm, RackForm, AssetForm, AssetCategoryForm, ResponsiblePersonForm
from .tasks import discover_hosts
import csv
import ipaddress
from .task_manager import task_manager
from django.views.decorators.csrf import csrf_exempt
import json
from .snmp_utils import SNMPClient
import logging
from datetime import datetime, timedelta
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Q, Count, Case, When, IntegerField
from django.core.paginator import Paginator
from django.db.models.functions import TruncDate
from django.contrib.auth.models import User, Group
import random
import string
from .utils.notifications import send_test_notification
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from django.core.exceptions import ValidationError

logger = logging.getLogger(__name__)

# Create your views here.

@login_required
def index(request):
    """首页视图"""
    # 获取所有主机
    hosts = Host.objects.all()
    total_hosts = hosts.count()
    
    # 获取启用状态的主机数量
    enabled_hosts = hosts.filter(status='enabled')
    enabled_count = enabled_hosts.count()
    
    # 获取禁用状态的主机数量
    disabled_hosts = hosts.filter(status='disabled')
    disabled_count = disabled_hosts.count()
    
    # 计算在线率
    online_rate = (enabled_count / total_hosts * 100) if total_hosts > 0 else 0
    
    # 获取资源占用TOP10的主机
    top_resource_hosts = Host.objects.filter(status='enabled').order_by('-cpu_usage', '-memory_usage')[:10]
    
    # 获取最近告警信息
    recent_alerts = Alert.objects.select_related('host').order_by('-created_at')[:10]
    
    # 获取告警统计数据
    critical_alerts_count = Alert.objects.filter(level='critical', status='active').count()
    urgent_alerts_count = Alert.objects.filter(level='urgent', status='active').count()
    warning_alerts_count = Alert.objects.filter(level='warning', status='active').count()
    info_alerts_count = Alert.objects.filter(level='info', status='active').count()
    
    context = {
        'host_count': enabled_count,  # 可用主机数量（状态为enabled）
        'disabled_count': disabled_count,  # 停用主机数量（状态为disabled）
        'total_hosts': total_hosts,  # 总主机数量
        'online_rate': round(online_rate, 1),  # 在线率（保留一位小数）
        
        # 添加新的上下文数据
        'top_resource_hosts': top_resource_hosts,  # 资源占用TOP10
        'recent_alerts': recent_alerts,  # 最近告警信息
        'critical_alerts_count': critical_alerts_count,  # 严重告警数量
        'urgent_alerts_count': urgent_alerts_count,  # 紧急告警数量
        'warning_alerts_count': warning_alerts_count,  # 警告告警数量
        'info_alerts_count': info_alerts_count,  # 一般告警数量
    }
    
    return render(request, 'monitor/index.html', context)

def login_view(request):
    if request.user.is_authenticated:
        return redirect('index')
    
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = authenticate(request, username=username, password=password)
        
        if user is not None:
            login(request, user)
            return redirect('index')
        else:
            messages.error(request, '用户名或密码错误')
    
    return render(request, 'monitor/login.html')

def logout_view(request):
    logout(request)
    return redirect('login')

def handle_alert(request, alert_id, action):
    if request.method != 'POST':
        return JsonResponse({'status': 'error', 'message': '方法不允许'}, status=405)
    
    alert = get_object_or_404(Alert, id=alert_id)
    
    if action == 'confirm':
        alert.status = 'confirmed'
    elif action == 'ignore':
        alert.status = 'ignored'
    else:
        return JsonResponse({'status': 'error', 'message': '无效的操作'}, status=400)
    
    alert.save()
    
    # 获取最新的告警列表
    recent_alerts = Alert.objects.select_related('host').order_by('-created_at')[:10]
    
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        return JsonResponse({
            'status': 'success',
            'message': f'告警已{action}',
            'alerts': list(recent_alerts.values('id', 'created_at', 'level', 'description', 'host__hostname'))
        })
    return redirect('index')

# 主机管理视图
class HostListView(LoginRequiredMixin, ListView):
    model = Host
    template_name = 'monitor/host_list.html'
    context_object_name = 'hosts'
    paginate_by = 10

class HostCreateView(LoginRequiredMixin, CreateView):
    model = Host
    form_class = HostForm
    template_name = 'monitor/host_form.html'
    success_url = reverse_lazy('host_list')

class HostUpdateView(LoginRequiredMixin, UpdateView):
    model = Host
    form_class = HostForm
    template_name = 'monitor/host_form.html'
    success_url = reverse_lazy('host_list')

class HostDeleteView(LoginRequiredMixin, DeleteView):
    model = Host
    template_name = 'monitor/host_confirm_delete.html'
    success_url = reverse_lazy('host_list')

# 网络设备管理视图
class NetworkDeviceListView(LoginRequiredMixin, ListView):
    model = NetworkDevice
    template_name = 'monitor/network_device_list.html'
    context_object_name = 'devices'
    paginate_by = 10

class NetworkDeviceCreateView(LoginRequiredMixin, CreateView):
    model = NetworkDevice
    form_class = NetworkDeviceForm
    template_name = 'monitor/network_device_form.html'
    success_url = reverse_lazy('network_device_list')

class NetworkDeviceUpdateView(LoginRequiredMixin, UpdateView):
    model = NetworkDevice
    form_class = NetworkDeviceForm
    template_name = 'monitor/network_device_form.html'
    success_url = reverse_lazy('network_device_list')

class NetworkDeviceDeleteView(LoginRequiredMixin, DeleteView):
    model = NetworkDevice
    template_name = 'monitor/network_device_confirm_delete.html'
    success_url = reverse_lazy('network_device_list')

def host_list(request):
    hosts = Host.objects.all().order_by('-id')
    
    # 如果是AJAX请求，更新SNMP数据
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        try:
            host_id = request.GET.get('host_id')
            if host_id:
                host = Host.objects.get(id=host_id)
                if host.monitor_type == 'snmp':
                    host.update_snmp_metrics()
                    return JsonResponse({
                        'status': 'success',
                        'data': {
                            'cpu_usage': host.snmp_cpu_usage,
                            'memory_usage': host.snmp_memory_usage,
                            'disk_usage': host.snmp_disk_usage,
                            'last_update': host.snmp_last_update.strftime('%Y-%m-%d %H:%M:%S') if host.snmp_last_update else ''
                        }
                    })
        except Exception as e:
            logger.error(f"更新SNMP数据失败: {str(e)}")
            return JsonResponse({
                'status': 'error',
                'message': str(e)
            })

    return render(request, 'monitor/host_list.html', {'hosts': hosts})

def host_add(request):
    if request.method == 'POST':
        form = HostForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, '主机添加成功')
            return redirect('host_list')
    else:
        # 获取预设的监控类型（如果在URL参数中指定）
        monitor_type = request.GET.get('type', 'ssh')
        form = HostForm(initial={'monitor_type': monitor_type})
    
    return render(request, 'monitor/host_form.html', {
        'form': form,
        'host': None  # 表示这是添加新主机
    })

def export_hosts(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="hosts.csv"'
    
    writer = csv.writer(response)
    writer.writerow(['主机名', 'IP地址', '监控类型', '状态'])
    
    hosts = Host.objects.all()
    for host in hosts:
        writer.writerow([
            host.hostname,
            host.ip_address,
            host.monitor_type,
            host.get_status_display()
        ])
    
    return response

def download_template(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="host_template.csv"'
    
    writer = csv.writer(response)
    writer.writerow(['主机名', 'IP地址', '监控类型', '用户名', '密码/社区字符串'])
    writer.writerow(['server1', '192.168.1.1', 'ipmi', 'admin', 'password'])
    writer.writerow(['server2', '192.168.1.2', 'ssh', 'root', 'password'])
    writer.writerow(['switch1', '192.168.1.3', 'snmp', 'public', ''])
    
    return response

def import_hosts(request):
    if request.method == 'POST' and request.FILES.get('file'):
        csv_file = request.FILES['file']
        decoded_file = csv_file.read().decode('utf-8').splitlines()
        reader = csv.DictReader(decoded_file)
        
        for row in reader:
            Host.objects.create(
                hostname=row['主机名'],
                ip_address=row['IP地址'],
                monitor_type=row['监控类型'],
                credentials={
                    'username': row['用户名'],
                    'password': row['密码/社区字符串']
                }
            )
        
        messages.success(request, '设备导入成功')
        return redirect('host_list')
    
    return render(request, 'monitor/import_hosts.html')

def auto_discover(request):
    if request.method == 'POST':
        network = request.POST.get('network')
        try:
            # 提交异步任务
            task_id = task_manager.submit_task(discover_hosts, network)
            messages.success(request, f'设备发现任务已启动 (任务ID: {task_id})')
            return JsonResponse({
                'status': 'success',
                'task_id': task_id,
                'message': '设备发现任务已启动'
            })
        except Exception as e:
            return JsonResponse({
                'status': 'error',
                'message': f'发现任务启动失败：{str(e)}'
            })
    
    # 获取最近的任务状态
    recent_tasks = task_manager.get_recent_tasks()
    return render(request, 'monitor/auto_discover.html', {'recent_tasks': recent_tasks})

def get_task_status(request, task_id):
    """
    获取任务状态的视图函数
    """
    status = task_manager.get_task_status(task_id)
    if status is None:
        return JsonResponse({'status': 'not_found'}, status=404)
    
    # 构建响应数据
    response_data = {
        'status': status.get('status'),
        'create_time': status.get('create_time'),
        'error': status.get('error', ''),
    }
    
    # 如果任务完成，添加结果
    if status.get('status') == 'completed' and 'result' in status:
        response_data['result'] = status['result']
    
    return JsonResponse(response_data)

@require_POST
def batch_delete(request):
    try:
        ids = request.POST.get('ids', '').split(',')
        Host.objects.filter(id__in=ids).delete()
        return JsonResponse({'status': 'success', 'message': '已删除选中的主机'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)}, status=400)

@require_POST
def batch_enable(request):
    try:
        ids = request.POST.get('ids', '').split(',')
        Host.objects.filter(id__in=ids).update(status='enabled')
        return JsonResponse({'status': 'success', 'message': '已启用选中的主机'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)}, status=400)

@require_POST
def batch_disable(request):
    try:
        ids = request.POST.get('ids', '').split(',')
        Host.objects.filter(id__in=ids).update(status='disabled')
        return JsonResponse({'status': 'success', 'message': '已禁用选中的主机'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)}, status=400)

def get_task_info(request):
    """获取任务管理器状态信息"""
    return JsonResponse({
        'active_tasks': task_manager.get_active_tasks_count(),
        'recent_tasks': task_manager.get_recent_tasks()
    })

def cancel_task(request, task_id):
    """取消任务"""
    if request.method == 'POST':
        success = task_manager.cancel_task(task_id)
        return JsonResponse({
            'status': 'success' if success else 'failed',
            'message': '任务已取消' if success else '无法取消任务'
        })
    return JsonResponse({'status': 'error', 'message': '方法不允许'}, status=405)

def host_detail(request, pk):
    host = get_object_or_404(Host, pk=pk)
    return render(request, 'monitor/host_detail.html', {'host': host})

@csrf_exempt
def host_edit(request, pk):
    host = get_object_or_404(Host, pk=pk)
    if request.method == 'POST':
        try:
            form = HostForm(request.POST, instance=host)
            if form.is_valid():
                host = form.save()
                
                # 如果是AJAX请求，返回JSON响应
                if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                    return JsonResponse({
                        'status': 'success',
                        'message': '主机信息已更新'
                    })
                
                messages.success(request, '主机信息已更新')
                return redirect('host_list')
            else:
                # 如果是AJAX请求，返回表单错误
                if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                    return JsonResponse({
                        'status': 'error',
                        'message': '表单验证失败',
                        'errors': form.errors
                    })
                
        except Exception as e:
            if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                return JsonResponse({
                    'status': 'error',
                    'message': str(e)
                })
            messages.error(request, f'更新失败：{str(e)}')
    else:
        form = HostForm(instance=host)
    
    return render(request, 'monitor/host_form.html', {
        'form': form,
        'host': host
    })

def host_delete(request, pk):
    host = get_object_or_404(Host, pk=pk)
    if request.method == 'POST':
        host.delete()
        messages.success(request, '主机已删除')
        return redirect('host_list')
    return render(request, 'monitor/host_confirm_delete.html', {'host': host})

@require_http_methods(["POST"])
def batch_update_hosts(request):
    try:
        data = json.loads(request.body)
        host_ids = data.get('host_ids', [])
        action = data.get('action')
        
        if not host_ids:
            return JsonResponse({'status': 'error', 'message': '未选择任何设备'})
            
        hosts = Host.objects.filter(id__in=host_ids)
        
        if action == 'enable':
            hosts.update(status='enabled')
            message = '启用'
        elif action == 'disable':
            hosts.update(status='disabled')
            message = '禁用'
        elif action == 'delete':
            hosts.delete()
            message = '删除'
        else:
            return JsonResponse({'status': 'error', 'message': '不支持的操作'})
            
        # 获取更新后的统计数据
        total_hosts = Host.objects.count()
        disabled_count = Host.objects.filter(status='disabled').count()
        host_count = Host.objects.filter(status='enabled').count()
        online_rate = round((host_count / total_hosts * 100), 2) if total_hosts > 0 else 0
        
        return JsonResponse({
            'status': 'success',
            'message': f'成功{message}选中的设备',
            'data': {
                'total_hosts': total_hosts,
                'disabled_count': disabled_count,
                'host_count': host_count,
                'online_rate': online_rate
            }
        })
        
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
def network_device_list(request):
    """网络设备列表视图"""
    devices = NetworkDevice.objects.all().order_by('-id')
    
    # 如果是AJAX请求，更新SNMP数据
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        device_id = request.GET.get('device_id')
        if device_id:
            device = get_object_or_404(NetworkDevice, id=device_id)
            if device.status == 'enabled':
                try:
                    device.update_snmp_metrics()
                    return JsonResponse({
                        'status': 'success',
                        'data': {
                            'cpu_usage': device.snmp_cpu_usage,
                            'memory_usage': device.snmp_memory_usage,
                            'last_update': device.snmp_last_update.strftime('%Y-%m-%d %H:%M:%S') if device.snmp_last_update else ''
                        }
                    })
                except Exception as e:
                    return JsonResponse({
                        'status': 'error',
                        'message': str(e)
                    })
    
    return render(request, 'monitor/network_device_list.html', {'devices': devices})

@login_required
def network_device_add(request):
    """添加网络设备视图"""
    if request.method == 'POST':
        form = NetworkDeviceForm(request.POST)
        if form.is_valid():
            device = form.save()
            return redirect('network_device_list')
    else:
        form = NetworkDeviceForm()
    
    return render(request, 'monitor/network_device_form.html', {'form': form})

@login_required
def network_device_edit(request, pk):
    """编辑网络设备视图"""
    device = get_object_or_404(NetworkDevice, pk=pk)
    if request.method == 'POST':
        form = NetworkDeviceForm(request.POST, instance=device)
        if form.is_valid():
            device = form.save()
            return redirect('network_device_list')
    else:
        form = NetworkDeviceForm(instance=device)
    
    return render(request, 'monitor/network_device_form.html', {
        'form': form,
        'device': device
    })

@login_required
@require_POST
def network_device_delete(request, pk):
    """删除网络设备视图"""
    device = get_object_or_404(NetworkDevice, pk=pk)
    device.delete()
    return JsonResponse({'status': 'success'})

@login_required
@require_POST
def batch_update_network_devices(request):
    """批量更新网络设备视图"""
    try:
        data = json.loads(request.body)
        device_ids = data.get('device_ids', [])
        action = data.get('action')
        
        if not device_ids:
            return JsonResponse({'status': 'error', 'message': '未选择任何设备'})
            
        devices = NetworkDevice.objects.filter(id__in=device_ids)
        
        if action == 'enable':
            devices.update(status='enabled')
            message = '启用'
        elif action == 'disable':
            devices.update(status='disabled')
            message = '禁用'
        elif action == 'delete':
            devices.delete()
            message = '删除'
        else:
            return JsonResponse({'status': 'error', 'message': '不支持的操作'})
        
        return JsonResponse({
            'status': 'success',
            'message': f'成功{message}选中的设备'
        })
        
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
def import_network_devices(request):
    """导入网络设备视图"""
    if request.method == 'POST' and request.FILES.get('file'):
        try:
            csv_file = request.FILES['file']
            decoded_file = csv_file.read().decode('utf-8').splitlines()
            reader = csv.DictReader(decoded_file)
            
            for row in reader:
                NetworkDevice.objects.create(
                    name=row['name'],
                    ip_address=row['ip_address'],
                    device_type=row['device_type'],
                    snmp_community=row['snmp_community'],
                    snmp_port=int(row['snmp_port']),
                    status=row['status']
                )
            
            return JsonResponse({'status': 'success', 'message': '设备导入成功'})
        except Exception as e:
            return JsonResponse({'status': 'error', 'message': str(e)})
    
    return render(request, 'monitor/import_network_devices.html')

@login_required
def export_network_devices(request):
    """导出网络设备视图"""
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = f'attachment; filename="network_devices_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv"'
    
    writer = csv.writer(response)
    writer.writerow(['name', 'ip_address', 'device_type', 'snmp_community', 'snmp_port', 'status'])
    
    devices = NetworkDevice.objects.all()
    for device in devices:
        writer.writerow([
            device.name,
            device.ip_address,
            device.device_type,
            device.snmp_community,
            device.snmp_port,
            device.status
        ])
    
    return response

@login_required
def download_network_template(request):
    """下载网络设备导入模板"""
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="network_device_template.csv"'
    
    writer = csv.writer(response)
    writer.writerow(['name', 'ip_address', 'device_type', 'snmp_community', 'snmp_port', 'status'])
    writer.writerow(['示例设备', '192.168.1.1', 'router', 'public', '161', 'enabled'])
    
    return response

@login_required
def idc_list(request):
    idcs = IDC.objects.all()
    return render(request, 'monitor/idc_list.html', {'idcs': idcs})

@login_required
def idc_detail(request, idc_id):
    idc = get_object_or_404(IDC, id=idc_id)
    racks = Rack.objects.filter(idc=idc).order_by('name')
    
    context = {
        'idc': idc,
        'racks': racks,
    }
    return render(request, 'monitor/idc_detail.html', context)

@login_required
def idc_3d_view(request, idc_id):
    idc = get_object_or_404(IDC, id=idc_id)
    cabinets_data = [
        {
            'x': cabinet.x_position,
            'z': cabinet.z_position,
            'number': cabinet.number,
            'servers': [
                {'height': server.height}
                for server in cabinet.servers.all()
            ],
            'switchboards': [
                {'height': switchboard.height}
                for switchboard in cabinet.switchboards.all()
            ]
        }
        for cabinet in idc.cabinets.all()
    ]
    
    context = {
        'idc': idc,
        'cabinets_data': json.dumps(cabinets_data),
        'device_types': DeviceType.objects.all().order_by('name'),
        'devices': Device.objects.all().order_by('name'),
    }
    
    return render(request, 'monitor/idc_3d.html', context)

@login_required
def idc_3d(request, idc_id):
    idc = get_object_or_404(IDC, id=idc_id)
    racks = Rack.objects.filter(idc=idc)
    
    # 准备机柜数据
    racks_data = []
    for rack in racks:
        racks_data.append({
            'id': rack.id,
            'name': rack.name,
            'x_position': float(rack.x_position),
            'y_position': float(rack.y_position),
            'status': rack.status,
            'status_color': rack.get_status_color(),
            'temperature': rack.temperature,
            'humidity': rack.humidity,
            'usage_percentage': rack.get_usage_percentage(),
            'power_percentage': rack.get_power_percentage(),
        })
    
    context = {
        'idc': idc,
        'racks_json': json.dumps(racks_data, cls=DjangoJSONEncoder)
    }
    return render(request, 'monitor/idc_3d.html', context)

@login_required
def get_device_types(request):
    """获取所有设备分类"""
    try:
        # 获取资产分类
        asset_categories = AssetCategory.objects.all()
        categories_data = []
        
        for category in asset_categories:
            category_data = {
                'id': category.id,
                'name': category.name,
            }
            categories_data.append(category_data)
        
        return JsonResponse({
            'status': 'success',
            'data': {
                'asset_categories': categories_data
            }
        })
        
    except Exception as e:
        logger.error(f'获取设备类型和资产分类时出错: {str(e)}')
        return JsonResponse({
            'status': 'error',
            'message': str(e)
        }, status=500)

def get_devices_by_type(request, type_id):
    """获取指定类型的设备列表"""
    try:
        # 确保type_id是有效的整数
        type_id = int(type_id)
        
        # 使用 category_id 替代 device_type_id
        assets = Asset.objects.filter(category_id=type_id).select_related('category')
        
        assets_data = []
        for asset in assets:
            asset_data = {
                'id': asset.id,
                'name': str(asset.name),
                'asset_number': asset.asset_number,
                'status': asset.status,
                'category': {
                    'id': asset.category.id,
                    'name': str(asset.category.name)
                } if asset.category else None,
                'up_rack_time': asset.up_rack_time.strftime('%Y-%m-%d') if asset.up_rack_time else '',
                'responsible_person': str(asset.responsible_person) if asset.responsible_person else ''
            }
            assets_data.append(asset_data)
        
        return JsonResponse({
            'status': 'success',
            'data': assets_data,
            'total_count': len(assets_data)
        })
        
    except ValueError:
        return JsonResponse({
            'status': 'error',
            'message': '无效的资产类型ID'
        }, status=400)
    except Asset.DoesNotExist:
        return JsonResponse({
            'status': 'error',
            'message': '未找到指定类型的资产'
        }, status=404)
    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': f'获取资产信息失败: {str(e)}'
        }, status=500)



@login_required
@permission_required('monitor.view_alertnotification')
def alert_notification(request):
    """告警通知配置视图"""
    notifications = AlertNotification.objects.all()
    return render(request, 'monitor/alert_notification.html', {
        'notifications': notifications
    })

@login_required
@permission_required('monitor.view_alertnotification')
def get_notification(request):
    """获取通知配置详情"""
    notification = get_object_or_404(AlertNotification, id=request.GET.get('id'))
    return JsonResponse({
        'id': notification.id,
        'name': notification.name,
        'type': notification.type,
        'alert_levels': notification.alert_levels,
        'config': notification.config
    })

@login_required
@permission_required('monitor.add_alertnotification')
@require_POST
def create_notification(request):
    """创建通知配置"""
    try:
        data = {
            'name': request.POST.get('name'),
            'type': request.POST.get('type'),
            'alert_levels': request.POST.getlist('alert_levels'),
            'is_active': True
        }
        
        # 根据类型设置配置
        if data['type'] == 'dingtalk':
            data['config'] = {
                'webhook_url': request.POST.get('dingtalk_webhook_url'),
                'secret': request.POST.get('dingtalk_secret')
            }
        elif data['type'] == 'feishu':
            data['config'] = {
                'webhook_url': request.POST.get('feishu_webhook_url'),
                'secret': request.POST.get('feishu_secret')
            }
        elif data['type'] == 'serverchan':
            data['config'] = {
                'sckey': request.POST.get('serverchan_sckey')
            }
        elif data['type'] == 'wecom':
            data['config'] = {
                'corp_id': request.POST.get('wecom_corp_id'),
                'corp_secret': request.POST.get('wecom_corp_secret'),
                'agent_id': request.POST.get('wecom_agent_id')
            }
        elif data['type'] == 'telegram':
            data['config'] = {
                'bot_token': request.POST.get('telegram_bot_token'),
                'chat_id': request.POST.get('telegram_chat_id')
            }
        # ... 保留原有的 email, sms, webhook 配置处理 ...
        
        AlertNotification.objects.create(**data)
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
@permission_required('monitor.change_alertnotification')
@require_POST
def update_notification(request):
    """更新通知配置"""
    try:
        notification = get_object_or_404(AlertNotification, id=request.POST.get('id'))
        notification.name = request.POST.get('name')
        notification.type = request.POST.get('type')
        notification.alert_levels = request.POST.getlist('alert_levels')
        
        # 更新配置
        if notification.type == 'email':
            notification.config = {
                'recipients': request.POST.get('email_recipients').split(',')
            }
        elif notification.type == 'sms':
            notification.config = {
                'phone_numbers': request.POST.get('phone_numbers').split(',')
            }
        elif notification.type == 'webhook':
            notification.config = {
                'url': request.POST.get('webhook_url'),
                'method': request.POST.get('webhook_method'),
                'headers': json.loads(request.POST.get('webhook_headers', '{}'))
            }
        
        notification.save()
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
@permission_required('monitor.delete_alertnotification')
@require_POST
def delete_notification(request):
    """删除通知配置"""
    try:
        notification = get_object_or_404(AlertNotification, id=request.POST.get('id'))
        notification.delete()
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
@permission_required('monitor.change_alertnotification')
@require_POST
def toggle_notification(request):
    """切换通知状态"""
    try:
        notification = get_object_or_404(AlertNotification, id=request.POST.get('id'))
        notification.is_active = request.POST.get('is_active') == 'true'
        notification.save()
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
@permission_required('monitor.change_alertnotification')
@require_POST
def test_notification(request):
    """测试通知配置"""
    try:
        notification = get_object_or_404(AlertNotification, id=request.POST.get('id'))
        send_test_notification(notification)
        return JsonResponse({'status': 'success', 'message': '测试通知已发送'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': f'发送失败：{str(e)}'})

@login_required
@permission_required('auth.view_user', raise_exception=True)
def user_management(request):
    """用户管理视图"""
    users = User.objects.all().order_by('-date_joined')
    groups = Group.objects.all()
    
    return render(request, 'monitor/user_management.html', {
        'users': users,
        'groups': groups,
    })

@login_required
@permission_required('auth.view_user', raise_exception=True)
def get_user(request):
    """获取用户信息"""
    user_id = request.GET.get('user_id')
    user = get_object_or_404(User, id=user_id)
    
    return JsonResponse({
        'id': user.id,
        'username': user.username,
        'first_name': user.first_name,
        'last_name': user.last_name,
        'email': user.email,
        'groups': [group.id for group in user.groups.all()],
    })

@login_required
@permission_required('auth.add_user', raise_exception=True)
@require_POST
def create_user(request):
    """创建用户"""
    try:
        username = request.POST.get('username')
        if User.objects.filter(username=username).exists():
            return JsonResponse({'status': 'error', 'message': '用户名已存在'})
        
        user = User.objects.create_user(
            username=username,
            password=request.POST.get('password'),
            email=request.POST.get('email'),
            first_name=request.POST.get('first_name'),
            last_name=request.POST.get('last_name')
        )
        
        # 添加用户组
        group_ids = request.POST.getlist('groups[]')
        user.groups.set(group_ids)
        
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
@permission_required('auth.change_user', raise_exception=True)
@require_POST
def update_user(request):
    """更新用户信息"""
    try:
        user_id = request.POST.get('user_id')
        user = get_object_or_404(User, id=user_id)
        
        user.email = request.POST.get('email')
        user.first_name = request.POST.get('first_name')
        user.last_name = request.POST.get('last_name')
        user.save()
        
        # 更新用户组
        group_ids = request.POST.getlist('groups[]')
        user.groups.set(group_ids)
        
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
@permission_required('auth.change_user', raise_exception=True)
@require_POST
def toggle_user_status(request):
    """切换用户状态"""
    try:
        user_id = request.POST.get('user_id')
        user = get_object_or_404(User, id=user_id)
        
        # 不允许禁用自己
        if user == request.user:
            return JsonResponse({'status': 'error', 'message': '不能修改自己的状态'})
        
        user.is_active = not user.is_active
        user.save()
        
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
@permission_required('auth.change_user', raise_exception=True)
@require_POST
def reset_password(request):
    """重置用户密码"""
    try:
        user_id = request.POST.get('user_id')
        user = get_object_or_404(User, id=user_id)
        
        # 生成随机密码
        password = ''.join(random.choices(string.ascii_letters + string.digits, k=12))
        user.set_password(password)
        user.save()
        
        return JsonResponse({'status': 'success', 'password': password})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
def reports(request):
    """报表视图"""
    report_type = request.GET.get('report_type', 'asset_status')
    
    # 获取日期范围
    end_date = request.GET.get('end_date')
    end_date = datetime.strptime(end_date, '%Y-%m-%d') if end_date else datetime.now()
    
    start_date = request.GET.get('start_date')
    start_date = datetime.strptime(start_date, '%Y-%m-%d') if start_date else (end_date - timedelta(days=30))
    
    context = {
        'report_type': report_type,
        'start_date': start_date,
        'end_date': end_date,
    }
    
    if report_type == 'asset_status':
        # 资产状态统计
        status_stats = Asset.objects.values('status').annotate(
            count=Count('id')
        ).order_by('status')
        
        total_assets = Asset.objects.count()
        report_data = []
        
        for stat in status_stats:
            status_display = dict(Asset.STATUS_CHOICES)[stat['status']]
            percentage = round((stat['count'] / total_assets * 100), 2) if total_assets > 0 else 0
            report_data.append({
                'status_display': status_display,
                'count': stat['count'],
                'percentage': percentage
            })
        
        context['report_title'] = '资产状态统计报表'
        
    elif report_type == 'alert_summary':
        # 告警统计
        alert_stats = Alert.objects.filter(
            created_at__range=(start_date, end_date)
        ).values('level').annotate(
            count=Count('id')
        ).order_by('level')
        
        total_alerts = Alert.objects.filter(
            created_at__range=(start_date, end_date)
        ).count()
        
        report_data = []
        for stat in alert_stats:
            level_display = dict(Alert.LEVEL_CHOICES)[stat['level']]
            percentage = round((stat['count'] / total_alerts * 100), 2) if total_alerts > 0 else 0
            report_data.append({
                'level_display': level_display,
                'count': stat['count'],
                'percentage': percentage
            })
            
        context['report_title'] = '告警统计报表'
        
    elif report_type == 'device_status':
        # 设备状态统计
        device_stats = Device.objects.values('device_type').annotate(
            online_count=Count(Case(
                When(status=True, then=1),
                output_field=IntegerField(),
            )),
            total_count=Count('id')
        ).order_by('device_type')
        
        report_data = []
        for stat in device_stats:
            offline_count = stat['total_count'] - stat['online_count']
            online_rate = round((stat['online_count'] / stat['total_count'] * 100), 2) if stat['total_count'] > 0 else 0
            report_data.append({
                'device_type': stat['device_type'],
                'online_count': stat['online_count'],
                'offline_count': offline_count,
                'total_count': stat['total_count'],
                'online_rate': online_rate
            })
            
        context['report_title'] = '设备状态统计报表'
    
    context['report_data'] = report_data
    return render(request, 'monitor/reports.html', context)

@login_required
def alerts(request):
    """告警列表视图"""
    # 获取筛选参数
    level = request.GET.get('level')
    status = request.GET.get('status')
    search = request.GET.get('search')
    
    # 基础查询集
    alerts = Alert.objects.all().order_by('-created_at')
    
    # 应用筛选条件
    if level:
        alerts = alerts.filter(level=level)
    if status:
        alerts = alerts.filter(status=status)
    if search:
        alerts = alerts.filter(
            Q(device__name__icontains=search) |
            Q(message__icontains=search)
        )
    
    # 分页
    paginator = Paginator(alerts, 10)  # 每页显示10条
    page = request.GET.get('page')
    alerts = paginator.get_page(page)
    
    context = {
        'alerts': alerts,
        'alert_levels': Alert.LEVEL_CHOICES,
        'alert_statuses': Alert.STATUS_CHOICES,
        'selected_level': level,
        'selected_status': status,
        'search_query': search,
    }
    
    return render(request, 'monitor/alerts.html', context)

@login_required
@require_POST
def acknowledge_alerts(request):
    """确认告警"""
    alert_ids = request.POST.getlist('alert_ids[]')
    try:
        Alert.objects.filter(id__in=alert_ids).update(status='acknowledged')
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
@require_POST
def clear_alerts(request):
    """清除告警"""
    alert_ids = request.POST.getlist('alert_ids[]')
    try:
        Alert.objects.filter(id__in=alert_ids).update(status='cleared')
        return JsonResponse({'status': 'success'})
    except Exception as e:
        return JsonResponse({'status': 'error', 'message': str(e)})

@login_required
def dashboard(request):
    """仪表盘视图"""
    # 获取基础统计数据
    context = {
        'total_assets': Asset.objects.count(),
        'online_devices': Device.objects.filter(status=True).count(),
        'alert_count': Alert.objects.filter(status='active').count(),
        'idc_count': IDC.objects.count(),
        'recent_alerts': Alert.objects.order_by('-created_at')[:5],
    }
    
    # 获取资产状态分布数据
    status_data = Asset.objects.values('status').annotate(
        count=Count('id')
    ).order_by('status')
    
    status_labels = []
    status_counts = []
    
    status_dict = dict(Asset.STATUS_CHOICES)
    for item in status_data:
        status_labels.append(status_dict[item['status']])
        status_counts.append(item['count'])
    
    context['status_labels'] = status_labels
    context['status_data'] = status_counts
    
    return render(request, 'monitor/dashboard.html', context)

@login_required
def dashboard_data(request):
    """获取仪表盘实时数据的API"""
    # 获取最新统计数据
    status_data = Asset.objects.values('status').annotate(
        count=Count('id')
    ).order_by('status')
    
    status_counts = []
    for item in status_data:
        status_counts.append(item['count'])
    
    data = {
        'total_assets': Asset.objects.count(),
        'online_devices': Device.objects.filter(status=True).count(),
        'alert_count': Alert.objects.filter(status='active').count(),
        'idc_count': IDC.objects.count(),
        'status_data': status_counts,
    }
    
    return JsonResponse(data)

def topology(request):
    """网络拓扑视图"""
    return render(request, 'monitor/topology.html')

@login_required
def add_idc(request):
    if request.method == 'POST':
        form = IDCForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, '机房添加成功！')
            return redirect('idc_list')
    return redirect('idc_list')

@login_required
def add_rack(request):
    if request.method == 'POST':
        idc_id = request.POST.get('idc_id')
        idc = get_object_or_404(IDC, id=idc_id)
        
        # 创建包含 idc 的初始数据
        initial_data = request.POST.copy()
        initial_data['idc'] = idc.id
        
        form = RackForm(initial_data)
        if form.is_valid():
            rack = form.save(commit=False)
            rack.idc = idc
            rack.save()
            messages.success(request, '机柜添加成功！')
            return redirect('idc_detail', idc_id=idc_id)
        else:
            messages.error(request, '表单验证失败：' + str(form.errors))
    return redirect('idc_list')

@login_required
def edit_rack(request, rack_id):
    rack = get_object_or_404(Rack, id=rack_id)
    
    if request.method == 'POST':
        form = RackForm(request.POST, instance=rack)
        if form.is_valid():
            form.save()
            messages.success(request, f'机柜 {rack.name} 更新成功！')
            return redirect('idc_detail', idc_id=rack.idc.id)
        else:
            messages.error(request, '表单验证失败：' + str(form.errors))
            return redirect('idc_detail', idc_id=rack.idc.id)
    
    # 如果是 GET 请求，返回机柜数据用于填充编辑表单
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        rack_data = {
            'name': rack.name,
            'position': rack.position,
            'capacity': rack.capacity,
            'power_capacity': rack.power_capacity,
            'x_position': rack.x_position,
            'y_position': rack.y_position,
            'status': rack.status,
            'temperature': rack.temperature,
            'humidity': rack.humidity,
            'power_usage': rack.power_usage,
            'used_units': rack.used_units,
        }
        return JsonResponse({'status': 'success', 'data': rack_data})
    
    return redirect('idc_detail', idc_id=rack.idc.id)

@login_required
def delete_rack(request, rack_id):
    rack = get_object_or_404(Rack, id=rack_id)
    idc_id = rack.idc.id
    
    if request.method == 'POST':
        rack_name = rack.name
        rack.delete()
        messages.success(request, f'机柜 {rack_name} 已成功删除！')
    else:
        messages.error(request, '删除操作必须使用 POST 请求。')
    
    return redirect('idc_detail', idc_id=idc_id)

@login_required
def get_rack_info(request, rack_id):
    """获取机柜详细信息的 API"""
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        try:
            rack = Rack.objects.get(id=rack_id)
            data = {
                'name': rack.name,
                'position': rack.position,
                'capacity': rack.capacity,
                'power_capacity': rack.power_capacity,
                'x_position': rack.x_position,
                'y_position': rack.y_position,
                'status': rack.status,
                'temperature': rack.temperature,
                'humidity': rack.humidity,
                'power_usage': rack.power_usage,
                'used_units': rack.used_units,
            }
            return JsonResponse({'status': 'success', 'data': data})
        except Rack.DoesNotExist:
            return JsonResponse({'status': 'error', 'message': '机柜不存在'}, status=404)
    return JsonResponse({'status': 'error', 'message': '无效的请求'}, status=400)

def topology_view(request):
    devices = Device.objects.all()
    connections = Connection.objects.all()
    
    return render(request, 'monitor/topology.html', {
        'devices': devices,
        'connections': connections
    })

def asset_management(request):
    """资产管理视图"""
    # 获取筛选参数
    category_id = request.GET.get('category')
    status = request.GET.get('status')
    search_query = request.GET.get('search')
    
    # 基础查询集
    assets = Asset.objects.all().order_by('-created_at')
    
    # 应用筛选条件
    if category_id:
        assets = assets.filter(category_id=category_id)
    if status:
        assets = assets.filter(status=status)
    if search_query:
        assets = assets.filter(
            Q(name__icontains=search_query) |
            Q(asset_number__icontains=search_query) |
            Q(responsible_person__icontains=search_query)
        )
    
    # 分页
    paginator = Paginator(assets, 10)  # 每页显示10条
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    
    # 获取所有分类供筛选使用
    categories = AssetCategory.objects.all()
    
    context = {
        'page_obj': page_obj,
        'categories': categories,
        'status_choices': Asset.STATUS_CHOICES,
        'selected_category': category_id,
        'selected_status': status,
        'search_query': search_query,
    }
    
    return render(request, 'monitor/asset_management.html', context)

def asset_create(request):
    """创建资产"""
    if request.method == 'POST':
        form = AssetForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, '资产创建成功！')
            return redirect('asset_management')
    else:
        form = AssetForm()
    
    return render(request, 'monitor/asset_form.html', {'form': form, 'title': '新增资产'})

def asset_edit(request, pk):
    """编辑资产"""
    asset = get_object_or_404(Asset, pk=pk)
    if request.method == 'POST':
        form = AssetForm(request.POST, instance=asset)
        if form.is_valid(        ):
            form.save()
            messages.success(request, '资产更新成功！')
            return redirect('asset_management')
    else:
        form = AssetForm(instance=asset)
    
    return render(request, 'monitor/asset_form.html', {'form': form, 'title': '编辑资产'})

def asset_delete(request, pk):
    """删除资产"""
    asset = get_object_or_404(Asset, pk=pk)
    if request.method == 'POST':
        asset.delete()
        messages.success(request, '资产已删除！')
        return redirect('asset_management')
    
    return render(request, 'monitor/asset_confirm_delete.html', {'asset': asset})

@login_required
@permission_required('monitor.add_assetcategory', raise_exception=True)
def asset_category_create(request):
    """创建资产分类"""
    if request.method == 'POST':
        form = AssetCategoryForm(request.POST)
        if form.is_valid():
            category = form.save()
            return JsonResponse({
                'status': 'success',
                'id': category.id,
                'name': category.name
            })
        return JsonResponse({
            'status': 'error',
            'errors': form.errors
        }, status=400)
    return JsonResponse({'status': 'error', 'message': '方法不允许'}, status=405)

@login_required
@permission_required('monitor.add_responsibleperson', raise_exception=True)
def responsible_person_create(request):
    """创建负责人"""
    if request.method == 'POST':
        form = ResponsiblePersonForm(request.POST)
        if form.is_valid():
            person = form.save()
            return JsonResponse({
                'status': 'success',
                'id': person.id,
                'name': str(person)
            })
        return JsonResponse({
            'status': 'error',
            'errors': form.errors
        }, status=400)
    return JsonResponse({'status': 'error', 'message': '方法不允许'}, status=405)

@require_POST
def test_smtp_config(request):
    try:
        # 获取SMTP配置信息
        smtp_host = request.POST.get('smtp_host')
        smtp_port = int(request.POST.get('smtp_port'))
        smtp_username = request.POST.get('smtp_username')
        smtp_password = request.POST.get('smtp_password')
        smtp_from_email = request.POST.get('smtp_from_email')
        smtp_use_tls = request.POST.get('smtp_use_tls') == 'true'
        test_email = request.POST.get('test_email')

        # 创建测试邮件
        msg = MIMEMultipart()
        msg['From'] = smtp_from_email
        msg['To'] = test_email
        msg['Subject'] = '监控系统SMTP配置测试'

        body = '这是一封测试邮件，用于验证SMTP配置是否正确。如果您收到这封邮件，说明SMTP配置成功。'
        msg.attach(MIMEText(body, 'plain'))

        # 连接SMTP服务器
        server = smtplib.SMTP(smtp_host, smtp_port)
        server.set_debuglevel(1)  # 启用调试

        if smtp_use_tls:
            server.starttls()

        server.login(smtp_username, smtp_password)
        server.send_message(msg)
        server.quit()

        return JsonResponse({
            'status': 'success',
            'message': '测试邮件发送成功'
        })

    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': f'SMTP配置测试失败: {str(e)}'
        })

@require_POST
def test_sms_config(request):
    try:
        # 获取短信配置信息
        api_url = request.POST.get('api_url')
        api_key = request.POST.get('api_key')
        api_secret = request.POST.get('api_secret')
        test_phone = request.POST.get('test_phone')

        # TODO: 实现具体的短信发送测试逻辑
        # 这里需要根据您使用的短信服务商来实现具体的发送逻辑

        return JsonResponse({
            'status': 'success',
            'message': '测试短信发送成功'
        })

    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': f'短信配置测试失败: {str(e)}'
        })

def get_devices(request, idc_id):
    """获取指定IDC的所有设备信息"""
    try:
        # 验证idc_id
        if not isinstance(idc_id, int) or idc_id <= 0:
            raise ValidationError(f'无效的IDC ID: {idc_id}')

        # 记录请求信息
        logger.info(f'正在获取IDC {idc_id} 的设备信息')
        
        # 确保IDC存在
        idc = get_object_or_404(IDC, id=idc_id)
        logger.info(f'找到IDC: {idc.name}')
        
        # 获取该IDC下所有机柜
        cabinets = Cabinet.objects.filter(idc=idc)
        logger.info(f'找到 {cabinets.count()} 个机柜')
        
        # 获取所有设备
        devices = Device.objects.filter(
            cabinet__in=cabinets
        ).select_related('cabinet')
        logger.info(f'找到 {devices.count()} 个设备')
        
        # 格式化设备数据
        devices_data = []
        for device in devices:
            try:
                device_data = {
                    'id': device.id,
                    'name': str(device.name),
                    'device_type': str(device.device_type),
                    'ip_address': str(device.ip_address),
                    'status': str(device.status),
                    'cabinet_id': device.cabinet_id,
                    'position_x': float(device.position_x or 0),
                    'position_y': float(device.position_y or 0),
                    'position_z': float(device.position_z or 0),
                }
                devices_data.append(device_data)
            except Exception as e:
                logger.error(f'处理设备 {device.id} 数据时出错: {str(e)}')
                continue
        
        response_data = {
            'status': 'success',
            'count': len(devices_data),
            'devices': devices_data
        }
        
        logger.info('设备数据获取成功')
        return JsonResponse(response_data)
        
    except ValidationError as e:
        logger.error(f'验证错误: {str(e)}')
        return JsonResponse({
            'status': 'error',
            'message': str(e)
        }, status=400)
    except IDC.DoesNotExist:
        logger.error(f'IDC {idc_id} 不存在')
        return JsonResponse({
            'status': 'error',
            'message': f'IDC {idc_id} 不存在'
        }, status=404)
    except Exception as e:
        logger.error(f'获取设备时发生错误: {str(e)}', exc_info=True)
        return JsonResponse({
            'status': 'error',
            'message': '获取设备数据时发生错误',
            'detail': str(e)
        }, status=500)

@login_required
@require_POST
def save_devices(request):
    """保存设备位置信息"""
    try:
        data = json.loads(request.body)
        devices = data.get('devices', [])
        
        for device_data in devices:
            device_id = device_data.get('id')
            if device_id:
                device = Device.objects.filter(id=device_id).first()
                if device:
                    device.position_x = device_data.get('position_x', device.position_x)
                    device.position_y = device_data.get('position_y', device.position_y)
                    device.position_z = device_data.get('position_z', device.position_z)
                    device.save()
        
        return JsonResponse({
            'status': 'success',
            'message': '设备位置保存成功'
        })
    except json.JSONDecodeError:
        return JsonResponse({
            'status': 'error',
            'message': '无效的JSON数据'
        }, status=400)
    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': f'保存失败: {str(e)}'
        }, status=500)

def get_cabinets(request):
    racks = Rack.objects.all()
    data = []
    
    for rack in racks:
        cabinet_data = {
            'id': rack.id,
            'number': rack.position,
            'servers': [],
            'switchboards': [],
            'used_units': rack.used_units,
            'total_units': rack.capacity,
            'x_position': rack.x_position,
            'y_position': rack.y_position,
        }
        data.append(cabinet_data)
    
    return JsonResponse(data, safe=False)




def get_cabinet_free_units(request, cabinet_id):
    """获取机柜的空余U位"""
    try:
        # 获取机柜信息
        cabinet = Rack.objects.get(id=cabinet_id)
        total_units = 42  # 标准机柜高度为42U
        
        # 获取该机柜中已占用的U位
        occupied_assets = Rack.objects.filter(
            id=cabinet_id
        ).values('name', 'position', 'used_units')
        
        # 计算已占用的U位
        occupied_positions = set()
        for asset in occupied_assets:
            position = asset.get('position', 0)
            height = asset.get('used_units', 0)
            if position and height:
                # 将设备占用的所有U位添加到集合中
                for u in range(position, position + height):
                    occupied_positions.add(u)
        
        # 计算空余U位
        free_units = []
        for u in range(1, total_units + 1):
            if u not in occupied_positions:
                free_units.append({
                    'position': u,
                    'cabinet_id': cabinet_id
                })
        
        # 返回空余U位信息和机柜信息
        return JsonResponse({
            'status': 'success',
            'data': {
                'free_units': free_units,
                'cabinet_info': {
                    'id': cabinet.id,
                    'name': cabinet.name,
                    'total_units': total_units,
                    'free_count': len(free_units)
                },
                'occupied_assets': list(occupied_assets)
            }
        })
        
    except Cabinet.DoesNotExist:
        return JsonResponse({
            'status': 'error',
            'message': '机柜不存在'
        }, status=404)
    except Exception as e:
        return JsonResponse({
            'status': 'error',
            'message': f'获取空余U位失败: {str(e)}'
        }, status=500)